// Copyright 2013 by Rod Moten
//
// This program is part of the Grambler project.
// 
// Grambler is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as 
// published by the Free Software Foundation, either version 3 of 
// the License, or (at your option) any later version.
//
// Grambler is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Grambler.  If not, see <http://www.gnu.org/licenses/>..
package edu.hendrix.grambler.grammars;

import edu.hendrix.grambler.AbstractActionImpl;
import edu.hendrix.grambler.Action;
import edu.hendrix.grambler.Tree;

/**
 * @author profmocs@gmail.com
 *
 */
public class BinaryArithmeticAction extends AbstractActionImpl {

	public static class NumberAction extends AbstractActionImpl {

		/* (non-Javadoc)
		 * @see edu.hendrix.grambler.Action#execute(edu.hendrix.grambler.Tree)
		 */
		@Override
		public Object execute(Tree tree) throws Throwable {
			return new Integer(tree.toString());
		}
	}
	
	public enum ArithmeticOperation implements Action{
		ADDITION(0,'+'),
		SUBTRACTION(0,'-'),
		MULTIPLICATION(1,'*'),
		DIVISION(1,'/'),
		POWER(1,'^'),
		;
		
		private final Number identity;
		private final char operator;

		/**
		 * @param identity
		 */
		private ArithmeticOperation(int identity, char operator) {
			this.identity = identity;
			this.operator = operator;
		}

		public Number getIdentity() {
			return identity;
		}
		
		public Number execute(Number lhs, Number rhs) {
			switch (operator) {
			case '+': return lhs.doubleValue() + rhs.doubleValue();
			case '-': return lhs.doubleValue() - rhs.doubleValue();
			case '*': return lhs.doubleValue() * rhs.doubleValue();
			case '/': return lhs.doubleValue() / rhs.doubleValue();
			case '^': return Math.pow(lhs.doubleValue(), rhs.doubleValue());
			}
			throw new RuntimeException ("operator not correct: " + operator);
		}


		/* (non-Javadoc)
		 * @see edu.hendrix.grambler.Action#execute(edu.hendrix.grambler.Tree)
		 */
		@Override
		public Object execute(Tree tree) throws Throwable {
			return this;
		}
	}
	
	private final String lhsName;
	private final String rhsName;
	private String operationName;
	
	/**
	 * @param operation
	 */
	public BinaryArithmeticAction(String lhsName, String operationName, String rhsName) {
		this.lhsName = lhsName;
		this.rhsName = rhsName;
		this.operationName = operationName;
	}


	/* (non-Javadoc)
	 * @see edu.hendrix.grambler.Action#execute(edu.hendrix.grambler.Tree)
	 */
	@Override
	public Number execute(Tree tree) throws Throwable {
		Number lhs = (Number) getActionResultFromNamedChild(tree, lhsName);
		Number rhs = (Number) getActionResultFromNamedChild(tree, rhsName);
		ArithmeticOperation operation = (ArithmeticOperation) getActionResultFromNamedChild(tree, operationName);
		if (operation == null) {
			return (lhs == null ? rhs : lhs);
		}
		return operation.execute(lhs == null ? operation.getIdentity() : lhs, rhs == null ? operation.getIdentity() : rhs);
	}
}
